package org.opens.lambda.collector;

import com.google.common.collect.Range;
import org.junit.Test;
import org.opens.lambda.UserEntity;

import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.stream.Collectors;

import static java.util.function.BinaryOperator.*;

public class CollectorsAction {

    /**
     * 说明:
     *      1. Collectors.averagingDouble()方法表示计算流中元素该字段的平均值.
     */
    @Test
    public void testMethod_averagingDouble() {
        List<UserEntity> list = UserEntity.getList(10);
        Double collect = list.stream().collect(Collectors.averagingDouble(UserEntity::getAge));
        System.out.println(collect);
    }

    /**
     * 说明:
     *      1. averagingInt方法也是计算流中元素的某个字段的平均值;
     *          - 但是需要注意的是: 返回值仍旧是Double类型.
     */
    @Test
    public void testMethod_averagingInt() {
        List<UserEntity> list = UserEntity.getList(10);
        Double collect = list.stream().collect(Collectors.averagingInt(UserEntity::getAge));
        System.out.println(collect);
    }

    /**
     * 说明:
     *      1. averagingLong方法的返回值仍旧是Double类型.
     */
    @Test
    public void testMethod_averagingLong() {
        List<UserEntity> list = UserEntity.getList(10);
        Double collect = list.stream().collect(Collectors.averagingLong(UserEntity::getAge));
        System.out.println(collect);
    }

    /**
     * 说明:
     *      1. collectingAndThen就是对collector处理的结果再做一次处理.
     *          - 在这个中是将结果集转换为字符串类型.
     */
    @Test
    public void testMethod_collectingAndThen() {
        List<UserEntity> list = UserEntity.getList(10);
        Double collect1 = list.stream().collect(Collectors.averagingLong(UserEntity::getAge));
        String collect = list.stream().collect(Collectors.collectingAndThen(Collectors.averagingDouble(UserEntity::getAge), x -> "the avg is " + x));
        System.out.println(collect + ", " + collect.getClass().getName());
    }

    /**
     * 功能:
     *      使用Collectors.collectingAndThen和Collections::unmodifiableList将List转换为不可变的集合.
     * 说明:
     *      1. 不可变的集合仍旧实现了迭代器接口, 可以正常迭代, 但是不能添加或者删除, 或者排序.
     *      2. 提高了程序的安全性, 但是有点不利于改动.
     *          - 但是对于要求安全系数高的程序还是有点用的.
     *      3. Collectors.collectingAndThen的原理就是将第一步得出的结果传递给第二个方法,
     *          - 因为在函数式编程的背景下, 用户可以自定义传入方法, 所以非常灵活, 就像拼积木一样.
     */
    @Test
    public void testMethod_toUnModifyList() {
        List<UserEntity> list = UserEntity.getList(10);
        List<UserEntity> unModifyList = list.stream().filter(x -> x.getAge() > 18).collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
        //unModifyList.add(UserEntity.getRandomUser());//上一步会将流转换为一个不可变的List子类, 这个类的所有改动元素的方法都会抛出异常.
        unModifyList.stream().forEach(System.out::println);
    }

    /**
     * 说明:
     *      1. Collectors也可以用来统计, 虽然stream也有统计功能, 但是:
     *          - Collector的counting还是一个Collector, 可以继续做操作.
     */
    @Test
    public void testMethod_counting() {
        List<UserEntity> list = UserEntity.getList(10);
        Long collect = list.stream().filter(x -> x.getAge() >= 18).collect(Collectors.counting());
        Long collect2 = list.stream().filter(x -> x.getAge() >= 18).count();
        System.out.println(collect);
        System.out.println(collect2);
    }

    /**
     * 功能:
     *      根据年龄对list进行分组, 并且统计每个分组中的人数.
     * 说明:
     *      1. 这样的实现方式使用传统方式也可以实现, 但是这里是使用函数式编程更加友好的实现.
     */
    @Test
    public void testMethod_groupByAndCounting() {
        List<UserEntity> list = UserEntity.getList(1000);
        Map<Integer, Long> collect = list.stream().collect(Collectors.groupingBy(UserEntity::getAge, Collectors.counting()));
        System.out.println(collect);
    }

    /**
     * 功能:
     *      使用UserEntity的sex字段进行分组, 并对分组后的数据进行统计.
     * 说明:
     *      1. collect是统计出他们的平均年龄, collect1是统计出个数信息, 和, 最小值, 最大值, 平均数.
     *      2. 其实理解Collector应该参照mysql中GroupBy之后的having操作, having操作是分别针对GroupBy之后的结果做一次运算, Collectors同理:
     *          - 如果没有进行Collectors.groupingBy操作, 可以将当前stream当成一个整体的分组, 也就是说只有一个分组.
     *      3. Collectors.summarizingInt方法会对分组中的数据进行完整的统计, 统计出个数信息, 和, 最小值, 最大值, 平均数;
     *          - 结果封装为java.util.IntSummaryStatistics.
     *          - 还有扩展方法summarizingLong, summarizingDouble等, 这些方法的参数分别限定了返回值接口, 也就是说如果方法的返回值是其他值, 则需要手动转换.
     */
    @Test
    public void testMethod_groupByAndAvg() {
        List<UserEntity> list = UserEntity.getList(100);
        Map<String, Double> collect = list.stream().collect(Collectors.groupingBy(UserEntity::getSex, Collectors.averagingDouble(UserEntity::getAge)));
        Map<String, IntSummaryStatistics> collect1 = list.stream().collect(Collectors.groupingBy(UserEntity::getSex, Collectors.summarizingInt(UserEntity::getAge)));
        //这种方式就是将分组后的数据再次执行处理时, 按照一定规则去统计, 但是好像并没有实际意义.
        Map<String, IntSummaryStatistics> collect2 = list.stream().collect(Collectors.groupingBy(UserEntity::getSex, Collectors.summarizingInt(x -> x.getName().indexOf('n') != -1 ? 1 : 0)));

        //System.out.println(list);
        System.out.println(collect);
        System.out.println(collect1);
        System.out.println(collect2);
    }

    /**
     * 功能:
     *      将分组后得结果转换为TreeMap.
     */
    @Test
    public void testMethod_groupByTranslateToTreeMap() {
        List<UserEntity> list = UserEntity.getList(100);
        TreeMap<String, IntSummaryStatistics> collect = list.stream().collect(Collectors.groupingBy(UserEntity::getSex, TreeMap::new, Collectors.summarizingInt(UserEntity::getAge)));
        System.out.println(collect);
    }

    /**
     * 说明:
     *      1. groupingByConcurrent和groupingBy方法的使用完全相同, 只是返回值不同. groupingByConcurrent是ConcurrentMap, groupingBy是HashMap.
     */
    @Test
    public void testMethod_groupingByConcurrent() {
        List<UserEntity> list = UserEntity.getList(100);
        ConcurrentMap<String, List<UserEntity>> collect = list.stream().collect(Collectors.groupingByConcurrent(UserEntity::getSex));
        ConcurrentMap<String, IntSummaryStatistics> collect1 = list.stream().collect(Collectors.groupingByConcurrent(UserEntity::getSex, Collectors.summarizingInt(UserEntity::getAge)));

        System.out.println(collect);
        System.out.println(collect1);
    }

    /**
     * 说明:
     *      1. 这个方法的测试是将最终的分组结果转换为ConcurrentSkipListMap.
     *          - ConcurrentSkipListMap是redis主要使用的数据结构, 这种数据结果是典型的空间换时间型结构, 在一定数据量下, 其查找速度与红黑树近似.
     */
    @Test
    public void testMethod_groupingByConcurrent2() {
        List<UserEntity> list = UserEntity.getList(100);
        ConcurrentSkipListMap<String, IntSummaryStatistics> collect = list.stream().collect(Collectors.groupingByConcurrent(UserEntity::getSex, ConcurrentSkipListMap::new, Collectors.summarizingInt(UserEntity::getAge)));
        System.out.println(collect);
    }

    /**
     * 说明:
     *      1. joining方法可以将流中的字符串进行拼接, 可选使用joining(CharSequence)进行插入间隔符号.
     *          - Guava的Joiner功能比这个更加强大.
     *      2. joining(",", "start_", "_end"))方法时给流中的每一个元素添加第一个参数作为间隔, 并且给最终结果添加前缀和后缀.
     * 输出:
     *      8Q6CTAHE2dOfrlM
     *      8Q6CT_AHE2d_OfrlM
     *      start_8Q6CT,AHE2d,OfrlM_end
     */
    @Test
    public void testMethod_join() {
        List<UserEntity> list = UserEntity.getList(3);
        String collect = list.stream().map(UserEntity::getName).collect(Collectors.joining());
        String collect1 = list.stream().map(UserEntity::getName).collect(Collectors.joining("_"));
        String collect2 = list.stream().map(UserEntity::getName).collect(Collectors.joining(",", "start_", "_end"));

        System.out.println(collect);
        System.out.println(collect1);
        System.out.println(collect2);
    }

    /**
     * 说明:
     *      1. 使用mapping搭配joining也可以实现这个功能.
     *          - 感觉上mapping的功能应该更加丰富, 不应该只是这么简单.
     */
    @Test
    public void testMethod_mapping() {
        List<UserEntity> list = UserEntity.getList(3);
        String collect = list.stream().collect(Collectors.mapping(UserEntity::getName, Collectors.joining(",")));
        String collect2 = list.stream().map(UserEntity::getName).collect(Collectors.joining(","));

        System.out.println(collect);
        System.out.println(collect2);
    }

    /**
     * 说明:
     *      1. 使用Collectors.maxBy的场景更多是在使用groupBy之后, 如果是单组数据, 直接使用stream即可.
     *      2. 其实Collector的设计目的更多是为了处理将stream进行分组后再进行操作.
     *          - 可以获取最大值, 最小值, 统计某个字段的信息.
     */
    @Test
    public void test() {
        List<UserEntity> list = UserEntity.getList(300);
        Optional<UserEntity> collect = list.stream().collect(Collectors.maxBy(Comparator.comparingInt(UserEntity::getAge)));
        Optional<UserEntity> collect2 = list.stream().max(Comparator.comparingInt(UserEntity::getAge));
        Map<String, Optional<UserEntity>> collect3 = list.stream().collect(Collectors.groupingBy(UserEntity::getSex, Collectors.maxBy(Comparator.comparingInt(UserEntity::getAge))));
        Map<String, Optional<UserEntity>> collect4 = list.stream().collect(Collectors.groupingBy(UserEntity::getSex, Collectors.minBy(Comparator.comparingInt(UserEntity::getAge))));

        System.out.println(collect);
        System.out.println(collect2);
        collect3.forEach((k, v) -> System.out.println(k + ": " + v.orElse(null)));
        collect4.forEach((k, v) -> System.out.println(k + ": " + v.orElse(null)));
    }

    /**
     * 功能:
     *      使用Collectors.partitioningBy进行分组.
     * 说明:
     *      1. Collectors.partitioningBy分组和Collectors..groupBy分组的区别就是:
     *          - Collectors.partitioningBy最终会根据真假值分为2组;
     *          - Collectors..groupBy分组的个数不定, 有可能是1组或者多组.
     *      2. java.util.stream.Collectors#partitioningBy(java.util.function.Predicate, java.util.stream.Collector)这个方法可以再分组上再次做操作, 并且这个操作是独立于每一个分组的.
     *          - 执行流程参考mysql的group by之后的having操作.
     * 输出:
     *      {false=[User{id=-315159919, name='cWkKC', age=21}, User{id=650182871, name='rLRb0', age=26}, User{id=1608582329, name='a7Ybh', age=29}, User{id=1845380533, name='z6V2E', age=20}, User{id=-2124011583, name='Ax6FZ', age=24}, User{id=-947078102, name='GhiXt', age=28}, User{id=478379592, name='m44AF', age=21}, User{id=1547734919, name='3gh0z', age=20}, User{id=1400516984, name='Cyh8I', age=26}, User{id=-1465224562, name='3yvqt', age=26}, User{id=-1456264561, name='SE7EG', age=29}, User{id=-2061660202, name='p2lKw', age=29}, User{id=-168251183, name='hc1VM', age=23}, User{id=2598477, name='cL80S', age=23}, User{id=1266119659, name='XUWZr', age=24}, User{id=1222497038, name='Zfz7T', age=23}, User{id=1310206727, name='9DsCi', age=26}, User{id=-1810495568, name='WQ6cA', age=28}, User{id=318172432, name='BWgC5', age=27}, User{id=-1933088054, name='7aXjY', age=29}, User{id=-704887388, name='sM9E3', age=21}, User{id=909709000, name='fRZIa', age=29}, User{id=-756597414, name='K51ch', age=29}, User{id=-103802997, name='iqy5w', age=21}, User{id=2086313098, name='1T7zM', age=25}, User{id=-964403025, name='NKR1T', age=27}, User{id=2105727115, name='bOmBm', age=20}, User{id=346920816, name='G0o34', age=23}, User{id=-93375952, name='JaC3k', age=22}, User{id=1544570473, name='hm31u', age=28}, User{id=-1885482783, name='W1XTf', age=26}, User{id=-176611851, name='6vOTq', age=21}, User{id=-33604206, name='noaAG', age=29}, User{id=-1301740603, name='EDsqh', age=24}, User{id=-1798697404, name='QgYEU', age=22}, User{id=-627168595, name='TPG37', age=28}, User{id=-1885110223, name='VwKZP', age=24}, User{id=510971349, name='OG61V', age=29}, User{id=770306142, name='2Prc2', age=26}, User{id=-1980355104, name='fXCU3', age=28}, User{id=-507498501, name='Ghlxh', age=29}, User{id=559441003, name='pOozm', age=29}, User{id=717817380, name='cdiUB', age=26}, User{id=-1875799137, name='33SJi', age=23}, User{id=-1227751321, name='zd85U', age=29}, User{id=-1096856330, name='yuyr0', age=21}, User{id=-1368137158, name='Oge0v', age=29}, User{id=368210760, name='S5XWA', age=22}, User{id=-1744821705, name='CZk6R', age=29}, User{id=-419151333, name='fHuaG', age=25}], true=[User{id=-1974242282, name='oCMQM', age=31}, User{id=-1371150980, name='CiHth', age=72}, User{id=-458827153, name='NUfBr', age=37}, User{id=-402431243, name='Qn1Sa', age=46}, User{id=-755263154, name='5oCJa', age=75}, User{id=1586466182, name='P1TWS', age=72}, User{id=1522317630, name='Ml9rb', age=53}, User{id=-599120363, name='ACuXo', age=43}, User{id=-1963512910, name='H6dDW', age=34}, User{id=-1467896596, name='q9WJd', age=66}, User{id=-1590659951, name='HNXwS', age=48}, User{id=1267709574, name='CA41k', age=33}, User{id=1797533101, name='IFEtL', age=73}, User{id=-621233042, name='Hrm3A', age=62}, User{id=-1848783615, name='ACdDP', age=50}, User{id=-613281083, name='pLCVe', age=67}, User{id=1512820108, name='lNPJ5', age=57}, User{id=-1549909930, name='30TPY', age=57}, User{id=1808095601, name='RNitD', age=36}, User{id=-1825043394, name='BOX12', age=52}, User{id=-1666548063, name='wNNEz', age=55}, User{id=-263475611, name='7KtZP', age=31}, User{id=-1179702540, name='eB695', age=62}, User{id=-1064700970, name='XSJwx', age=70}, User{id=-885710864, name='S1K1R', age=60}, User{id=1244640010, name='MGKfP', age=42}, User{id=1090796480, name='vaITU', age=45}, User{id=580830849, name='S2KDL', age=30}, User{id=773560000, name='VCo7c', age=37}, User{id=1555971215, name='G8Qim', age=32}, User{id=-1788042495, name='Ht6lS', age=33}, User{id=-2013840499, name='jYo0s', age=56}, User{id=562381259, name='j6x2t', age=78}, User{id=-468936358, name='u2vVX', age=31}, User{id=-1878804002, name='U3qmc', age=34}, User{id=-239732346, name='Zh7xd', age=41}, User{id=-990996036, name='zSLDT', age=56}, User{id=732935805, name='mk8oE', age=74}, User{id=1806338398, name='iRWW6', age=37}, User{id=949768191, name='Dw0M5', age=34}, User{id=1036540698, name='o2EzU', age=36}, User{id=-169097858, name='lWYMD', age=63}, User{id=-583066291, name='7yOS9', age=79}, User{id=-288686160, name='4tgD3', age=76}, User{id=1482760632, name='EPcy0', age=73}, User{id=-1355915356, name='nEgZ8', age=50}, User{id=-1355867061, name='GM4J7', age=74}, User{id=-870571427, name='M4apa', age=47}, User{id=-1934736899, name='bqSj7', age=66}, User{id=697268445, name='GKAiq', age=47}, User{id=-938201775, name='8nZY8', age=61}, User{id=-1614960248, name='BaaNU', age=53}, User{id=337347287, name='AYJe0', age=43}, User{id=1267641669, name='vX0LY', age=47}, User{id=1318181888, name='3TKM8', age=70}, User{id=-135886925, name='oO4dg', age=59}, User{id=638853781, name='fqKnd', age=39}, User{id=-705347066, name='n6cak', age=60}, User{id=1102548757, name='6BkhP', age=69}, User{id=1141737977, name='kXnOg', age=48}, User{id=-1188320483, name='5CSZg', age=30}, User{id=-95355838, name='Xee5f', age=79}, User{id=-56787035, name='f4FZG', age=68}, User{id=1881184314, name='RuT0p', age=59}, User{id=955559216, name='mDWTL', age=64}, User{id=1326250880, name='MrhOt', age=40}, User{id=-892512248, name='3ct3Q', age=43}, User{id=-694552934, name='WqRF4', age=57}, User{id=-1030059444, name='D4QIg', age=71}, User{id=-1098748850, name='3w4Au', age=71}, User{id=-600631630, name='iBna8', age=43}, User{id=-104885755, name='2sr1A', age=58}, User{id=-105198147, name='Vm8Lr', age=46}, User{id=-2146726176, name='p3jsz', age=40}, User{id=-1078126080, name='60b0C', age=49}, User{id=1718134377, name='1uTKR', age=43}, User{id=943557346, name='mpaXE', age=73}, User{id=-2029132814, name='cy5MS', age=63}, User{id=216876771, name='0wmoK', age=60}, User{id=-1402396636, name='lufEL', age=64}, User{id=1235001621, name='jj2gh', age=53}, User{id=-1591637574, name='jFFTf', age=30}, User{id=-708584065, name='gwpZJ', age=52}, User{id=-2055424392, name='S1nss', age=63}, User{id=184047528, name='hQHJw', age=55}, User{id=-774678617, name='E90Ce', age=66}, User{id=1229613427, name='UH2Z7', age=61}, User{id=1048660689, name='VzaYb', age=72}, User{id=652153859, name='t493d', age=72}, User{id=-1746600995, name='OoNo3', age=73}, User{id=-1383811665, name='16ZNV', age=60}, User{id=-758537540, name='dOXE3', age=74}, User{id=-853528916, name='NcBs7', age=38}, User{id=-188135420, name='SR1Ly', age=55}, User{id=-863579118, name='CCQED', age=74}, User{id=-1817476061, name='Z1SlK', age=40}, User{id=-134879032, name='QS9Dr', age=32}, User{id=613633526, name='26Z9g', age=74}, User{id=1467106345, name='OW9up', age=57}, User{id=1064083822, name='mt2uE', age=49}, User{id=1931170285, name='D3fny', age=37}, User{id=300429662, name='f15Yv', age=43}, User{id=1578962165, name='vwLU1', age=62}, User{id=1375860082, name='VXjfO', age=43}, User{id=675438904, name='5EkCC', age=75}, User{id=-1772470008, name='96ar8', age=59}, User{id=499031153, name='Krx5w', age=40}, User{id=-1297869658, name='CmIQ5', age=63}, User{id=-1905918350, name='EtAkj', age=32}, User{id=1605221298, name='0CGaT', age=62}, User{id=-1543950137, name='52QkS', age=33}, User{id=-2133220513, name='Cw3gh', age=45}, User{id=-1448194171, name='4hW3a', age=34}, User{id=-1220989376, name='v6DNW', age=30}, User{id=226792561, name='YLPyR', age=59}, User{id=-538316452, name='XIi3m', age=51}, User{id=-1051470172, name='jcuPj', age=33}, User{id=738576433, name='D880T', age=50}, User{id=-564550638, name='jHcQo', age=31}, User{id=2032536478, name='HV6HW', age=63}, User{id=-106795571, name='qTWWO', age=32}, User{id=2017115249, name='1JGyz', age=71}, User{id=27212070, name='7zhsp', age=51}, User{id=1474342032, name='1qQji', age=61}, User{id=913077997, name='tVr6Z', age=69}, User{id=-1510729239, name='6cRta', age=30}, User{id=908285829, name='3JdFC', age=52}, User{id=-1262070599, name='5re2q', age=48}, User{id=1173355908, name='CH5sm', age=59}, User{id=1293352648, name='jCYX9', age=62}, User{id=1503765019, name='h1ePl', age=44}, User{id=2070266790, name='lCbc8', age=30}, User{id=-1138694790, name='PyTLz', age=42}, User{id=-1422054892, name='lotsJ', age=51}, User{id=-1187482906, name='YHV4v', age=71}, User{id=-819012329, name='cS2ah', age=41}, User{id=-1844534463, name='BCpku', age=35}, User{id=-1273808596, name='eL504', age=44}, User{id=1193808176, name='0ILVD', age=72}, User{id=292407567, name='up7bZ', age=49}, User{id=-466991036, name='wxKNR', age=44}, User{id=-221481458, name='2UfIN', age=52}, User{id=1009116315, name='CWkZ5', age=58}, User{id=-1715582182, name='QFnvD', age=69}, User{id=-1274560379, name='6I0Hb', age=49}, User{id=2010099990, name='sSEi7', age=76}, User{id=173679744, name='LpDnc', age=59}, User{id=1852257134, name='OAb6j', age=69}, User{id=-1313591186, name='ySKV8', age=53}, User{id=1557364459, name='wEZT2', age=62}, User{id=1945118204, name='y4KC5', age=53}, User{id=-451398899, name='L2MP7', age=60}, User{id=1005722628, name='dLiAX', age=78}, User{id=-762895478, name='I1N2c', age=71}, User{id=-553796220, name='yn3Ts', age=35}, User{id=964945898, name='V47pU', age=49}, User{id=652904522, name='2prZQ', age=35}, User{id=24523339, name='R4hkl', age=53}, User{id=2052816223, name='8uK7m', age=64}, User{id=187011056, name='c5fJ1', age=36}, User{id=-1262750312, name='wyAwd', age=55}, User{id=405643368, name='jw8tF', age=51}, User{id=-287487313, name='eMUOV', age=74}, User{id=-983450426, name='vsjPk', age=77}, User{id=-755132009, name='dExXB', age=63}, User{id=-1716751988, name='aP9sd', age=70}, User{id=2101999417, name='calU9', age=44}, User{id=998088664, name='5i7NC', age=67}, User{id=-1941464418, name='cfcDq', age=41}, User{id=-182582249, name='z8PCL', age=62}, User{id=-782316610, name='Ezh8z', age=57}, User{id=1352193984, name='3QWXa', age=45}, User{id=2092600100, name='qbXic', age=79}, User{id=269003581, name='i06jj', age=66}, User{id=1450965187, name='ZHT9G', age=37}, User{id=-831205715, name='0glQ7', age=51}, User{id=-860379293, name='5BGpR', age=46}, User{id=223147805, name='9739i', age=60}, User{id=1751451330, name='oIt65', age=45}, User{id=764913133, name='Zgt1S', age=46}, User{id=-2010575241, name='4F5Gr', age=74}, User{id=-1848599887, name='HStIp', age=53}, User{id=781955281, name='rLfmY', age=64}, User{id=589297587, name='svSIi', age=34}, User{id=604318131, name='BR5LO', age=62}, User{id=192188791, name='zTjcW', age=62}, User{id=-1322208870, name='1zQRf', age=46}, User{id=593336562, name='Z9STR', age=54}, User{id=1298307928, name='Txo4I', age=60}, User{id=1429345075, name='iS11y', age=51}, User{id=-1652134107, name='H5OFv', age=73}, User{id=-845759832, name='eoUyR', age=60}, User{id=281395210, name='zBRFt', age=68}, User{id=-1573534387, name='jOtCr', age=30}, User{id=736973030, name='g2Yf2', age=43}, User{id=1220424819, name='v3dMK', age=68}, User{id=-2108756628, name='NqoIV', age=66}, User{id=-1961232371, name='W7cB5', age=51}, User{id=-1538708682, name='HcUYX', age=63}, User{id=1182196051, name='kHHIz', age=35}, User{id=1881392650, name='Ad4Ue', age=41}, User{id=-1404364737, name='Fe5Bw', age=70}, User{id=823369495, name='LQmmI', age=77}, User{id=-2110890657, name='6htnN', age=57}, User{id=1842693636, name='76p8P', age=55}, User{id=881438056, name='4pf7d', age=76}, User{id=1722040306, name='K5nHO', age=50}, User{id=2065762474, name='4jEM9', age=67}, User{id=619137718, name='wKcY4', age=30}, User{id=-1730239305, name='RbR7N', age=69}, User{id=-676765964, name='RoP4Y', age=64}, User{id=-1726270478, name='xaF9F', age=79}, User{id=-496391793, name='qeduD', age=74}, User{id=757306294, name='CSnsM', age=38}, User{id=-1912963385, name='WVmvJ', age=67}, User{id=552428594, name='FPktf', age=40}, User{id=1449200506, name='ghBcV', age=52}, User{id=-1778050212, name='7qOND', age=34}, User{id=1728207140, name='XMe0w', age=40}, User{id=-2003086256, name='xmfIq', age=64}, User{id=1030082457, name='a0b0d', age=34}, User{id=-199998129, name='q4OHC', age=35}, User{id=-1741589676, name='hLXSX', age=68}, User{id=1549579373, name='IuzbT', age=77}, User{id=-1049029968, name='37teL', age=76}, User{id=-1408874412, name='T4ZW4', age=40}, User{id=-1746597139, name='JcrLf', age=65}, User{id=1215406189, name='Dpafx', age=47}, User{id=-1315250035, name='agBoE', age=54}, User{id=1367512949, name='FHf4L', age=41}, User{id=-1518974045, name='fR8Qb', age=73}, User{id=2134851166, name='3pRqm', age=72}, User{id=706715228, name='QreZa', age=66}, User{id=1845604953, name='HfWNB', age=66}, User{id=162658776, name='whMID', age=53}, User{id=1049012994, name='6wXXi', age=63}, User{id=-837256891, name='GBgzS', age=77}, User{id=2140536682, name='qyiwy', age=79}, User{id=1637739735, name='gJfS7', age=53}, User{id=613425305, name='KScGd', age=32}, User{id=-887826569, name='cynrX', age=42}, User{id=-1132774663, name='Rm6NX', age=42}, User{id=-1774002370, name='HlcSf', age=35}, User{id=-162774714, name='2vwKm', age=75}, User{id=-118380197, name='ObUwL', age=61}, User{id=1683208651, name='GNMYF', age=51}, User{id=1497317259, name='1Mue8', age=62}, User{id=106606368, name='8ew0R', age=52}, User{id=-705301985, name='P449U', age=55}, User{id=606216867, name='g7Gse', age=49}]}
     *      {false=IntSummaryStatistics{count=62, sum=1527, min=20, average=24.629032, max=29}, true=IntSummaryStatistics{count=238, sum=13069, min=30, average=54.911765, max=79}}
     *
     */
    @Test
    public void testMethod_collectorsPartitioningBy() {
        List<UserEntity> list = UserEntity.getList(300);
        Map<Boolean, List<UserEntity>> collect = list.stream().collect(Collectors.partitioningBy(x -> x.getAge() >= 30));
        System.out.println(collect);

        Map<Boolean, IntSummaryStatistics> collect1 = list.stream().collect(Collectors.partitioningBy(x -> x.getAge() >= 30, Collectors.summarizingInt(UserEntity::getAge)));
        System.out.println(collect1);
    }

    /**
     * 功能:
     *      使用collect的reducing进行聚合操作.
     * 说明:
     *      1. java.util.stream.Collectors#reducing(java.lang.Object, java.util.function.BinaryOperator)方法的第一个参数相当于是聚合操作的初始值, 第二个参数定义聚合操作的细节.
     *          - 可以把聚合操作看成一个迭代过程, 而迭代子就是流中的元素, collect函数可以看成是一种迭代的声明;
     *          - 可以用map将流转换为需要的类型.
     *      2. 可以使用java.util.stream.Collectors#reducing(java.lang.Object, java.util.function.Function, java.util.function.BinaryOperator)替换掉stream的map方法, 自己定义转换规则(是一个Function).
     * 输出:
     *      Optional[User{id=1884191090, name='TMkLi', age=79}]
     *      14650
     *      14650
     */
    @Test
    public void testMethod_collectorsReducing() {
        List<UserEntity> list = UserEntity.getList(300);
        Optional<UserEntity> collect = list.stream().collect(Collectors.reducing(maxBy(Comparator.comparingInt(UserEntity::getAge))));

        System.out.println(collect);

        Integer collect1 = list.stream().map(UserEntity::getAge).collect(Collectors.reducing(0, (x, y) -> x + y));
        System.out.println(collect1);

        Integer collect2 = list.stream().collect(Collectors.reducing(0, UserEntity::getAge, (x, y) -> x + y));
        System.out.println(collect2);
    }

    /**
     * 功能:
     *      对流中的结果进行统计.
     * 说明:
     *      1. 会发生隐式转换, 但是最好能类型匹配, 避免丢失精度.
     * 输出:
     *      IntSummaryStatistics{count=300, sum=14685, min=20, average=48.950000, max=79}
     *      DoubleSummaryStatistics{count=300, sum=14685.000000, min=20.000000, average=48.950000, max=79.000000}
     *      LongSummaryStatistics{count=300, sum=14685, min=20, average=48.950000, max=79}
     */
    @Test
    public void testMethod_summarizingInt() {
        List<UserEntity> list = UserEntity.getList(300);
        IntSummaryStatistics collect1 = list.stream().collect(Collectors.summarizingInt(UserEntity::getAge));
        DoubleSummaryStatistics collect2 = list.stream().collect(Collectors.summarizingDouble(x -> x.getAge()));
        LongSummaryStatistics collect3 = list.stream().collect(Collectors.summarizingLong(x -> x.getAge()));

        System.out.println(collect1);
        System.out.println(collect2);
        System.out.println(collect3);
    }

    /**
     * 功能:
     *      如果是单组数据, 直接使用流的特性就可以解决问题, 没有必要使用Collectors.
     * 注意:
     *      1. sum()方法时IntStream特有的方法, 所以要使用mapToInt来对流中的元素做提取.
     */
    @Test
    public void testMethod_mapToInt() {
        List<UserEntity> list = UserEntity.getList(300);
        int sum = list.stream().mapToInt(UserEntity::getAge).sum();
        System.out.println(sum);
    }

    /**
     * 功能:
     *      传入一个Collection接口的实现的构造函数, 则collect方法会自动帮助我们将流中的元素封装到其中.
     * 注意:
     *      1. 构造函数的引用看起来比较怪, 但是可以将其理解为new一个对象有返回值.
     *          - 构造函数的引用并未深入理解.
     *
     */
    @Test
    public void testMethod_toCollection() {
        List<UserEntity> list = UserEntity.getList(300);
        LinkedList<UserEntity> collect = list.stream().filter(x -> x.getName().startsWith("N")).collect(Collectors.toCollection(LinkedList::new));
        System.out.println(collect);

        System.out.println();
    }

    /**
     * 功能:
     *      使用toConcurrentMap实现统计, 这种写法较难理解.
     * 说明:
     *      1. 可以用stream的count或者分组后得Collector.counting()来替代这种写法, 因为这种写法真的不好用.
     */
    @Test
    public void testMethod_() {
        List<UserEntity> list = UserEntity.getList(3);
        Map<String, Long> collect = list.stream().collect(Collectors.groupingBy(UserEntity::getSex, Collectors.counting()));
        System.out.println(collect);

        ConcurrentMap<String, Integer> collect1 = list.stream().collect(Collectors.toConcurrentMap(UserEntity::getName, UserEntity::getAge));
        System.out.println(collect1);

        ConcurrentMap<String, Long> collect2 = list.stream().collect(Collectors.toConcurrentMap(UserEntity::getSex, a -> 1L, Long::sum));
        System.out.println(collect);
    }

    @Test
    public void testMethod_toList() {
        List<UserEntity> list = UserEntity.getList(3);
        List<UserEntity> collect = list.stream().filter(x -> x.getAge() > 30).collect(Collectors.toList());

        Set<UserEntity> collect1 = list.stream().filter(x -> "男".equals(x.getSex())).collect(Collectors.toSet());

        Map<String, Integer> collect2 = list.stream().collect(Collectors.toMap(UserEntity::getName, UserEntity::getAge));

    }

    /**
     * 功能:
     *      这个例子是一个实际应用.
     * 说明:
     *      1. collect2结果的得来是这样的:
     *          - 先根据UserEntity::getSex字段进行分组;
     *          - 然后对分组中的每一个元素再次根据年龄进行分组.
     */
    @Test
    public void testMethod_1() {
        List<UserEntity> list = UserEntity.getList(30);
        Map<Integer, List<UserEntity>> collect = list.stream().collect(Collectors.groupingBy(UserEntity::getAge));
        System.out.println(collect);

        Map<Integer, Long> collect1 = list.stream().collect(Collectors.groupingBy(UserEntity::getAge, Collectors.counting()));

        Map<String, Map<String, List<UserEntity>>> collect2 = list.stream().collect(Collectors.groupingBy(UserEntity::getSex, Collectors.groupingBy(x -> {
            Integer age = x.getAge();
            Range<Integer> range0_17  = Range.openClosed(0, 17);
            Range<Integer> range18_30 = Range.openClosed(17, 30);
            Range<Integer> range30_60 = Range.openClosed(30, 60);
            if (range0_17.contains(age)) {
                return "0-17";
            } else if (range18_30.contains(age)) {
                return "18-29";
            } else if (range30_60.contains(age)) {
                return "30-59";
            } else {
                return "60+";
            }
        })));
        System.out.println(collect2);

        collect2.forEach((k, v) -> {
            v.forEach((x, y) -> {
                System.out.println(y);
            });
        });
    }

    /**
     * 说明：
     * <pre>
     *     1. 使用Collectors.maxBy方法获取流中的最大/最小元素；
     * </pre>
     */
    @Test
    public void test401() {
        List<UserEntity> list = UserEntity.getList(10);
        Optional<UserEntity> maxVal1 = list.stream().min(Comparator.comparingInt(UserEntity::getAge));
        Optional<UserEntity> maxVal2 = list.stream().collect(Collectors.maxBy(Comparator.comparingInt(UserEntity::getAge)));

        maxVal1.ifPresent(System.out::println);
        maxVal2.ifPresent(System.out::println);
    }

}
